package com.demo.client;

import java.util.List;
import java.util.UUID;
import java.util.concurrent.Future;

import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketIDFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Registration;
import org.jivesoftware.smack.provider.ProviderManager;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Handler;
import android.util.Log;


public class XmppManager {

	private static final String TAG = LogUtil.getClassName(XmppManager.class);
	
	private Context context = null;
	
	private SharedPreferences sharePre = null;
	
	private String username = null;
	private String password = null;
	private String xmppHost = null;
	private int xmppPort;
	
	private boolean running = false;
	private Future<?> futureTask;
	private List<Runnable> tasklist = null;
	private NotificationService.TaskSubmit taskSubmit = null;
	private NotificationService.TaskTracker taskTracker = null;
	
	private ConnectionListener connectionListener = null;
	private NotificationPacketListener notificationPacketListener =null;
	
	private XMPPConnection connection = null;
	private ReconnectionThread reconnection = null;
	
	private Handler handler;
	
	public XmppManager(NotificationService notificationService)
	{
		this.context = notificationService;
		
		sharePre = this.context.getSharedPreferences(Constants.SHARE_PREFERENCE_NAME, Context.MODE_PRIVATE);
		username = sharePre.getString(Constants.XMPP_USERNAME, "");
		password = sharePre.getString(Constants.XMPP_PASSWORD, "");
		xmppHost = sharePre.getString(Constants.XMPP_HOST, "localhost");
		xmppPort = sharePre.getInt(Constants.XMPP_PORT, 5222);
		
		taskSubmit = notificationService.getTaskSubmit();
		taskTracker = notificationService.getTaskTracker();
		
		connectionListener = new PersistentConnectionListener(this);
		reconnection = new ReconnectionThread(this);
	}
	public Context getContext() {

		return this.context;
	}

	private boolean isRegister()
	{
		return sharePre.contains(Constants.XMPP_USERNAME)
					&&sharePre.contains(Constants.XMPP_PASSWORD);
	}
	
	
	/**
	 * 把改task 线程加入列表
	 * 
	 * @param task
	 */
	private void addTask(Runnable task)
	{
		Log.d(TAG, "addTask(Runnable)...");
		taskTracker.increase();
		synchronized (tasklist) {
			if(tasklist.isEmpty()&&!running)
			{//如果列表为空 ,并且该task不是正在运行的
				running = true;
				futureTask = taskSubmit.submit(task);
				if(futureTask == null)
				{
					taskTracker.decrease();
				}
			}
			else{
				tasklist.add(task);
			}
		}
		Log.d(TAG, "addTask(Runnable) ..done");
	}
	private List<Runnable> getTaskList()
	{
		return tasklist;
	}
	
	/**
	 * 1.把进程事件放入到executor里，排队运行
	 * <br>2.计数器计线程的个数
	 */
	private void runTask() {
		Log.d(TAG, "run task.....");
		
		synchronized (tasklist) {
			running = false;
			futureTask = null;
			if(!tasklist.isEmpty())
			{
				Runnable runnable = (Runnable) tasklist.get(0);
				tasklist.remove(0);
				running = true;
				futureTask = taskSubmit.submit(runnable);
				if(futureTask == null)
					taskTracker.decrease();
			}
		}
		taskTracker.decrease();
	}
	
	public void disConnect() {
		terminatePersistentConnection();
	}
	
	public void connect()
	{
		submitLoginTask();
	}
	
	public class ConnectTask implements Runnable
	{
		private XmppManager xmppManager = null;

		public ConnectTask()
		{
			xmppManager = XmppManager.this;
		}
		
		@Override
		public void run() {
			Log.i(TAG, "ConnectTask.run()...");
			
			if(!xmppManager.isConnected())
			{
				ConnectionConfiguration config = 
					new ConnectionConfiguration(xmppHost,xmppPort);
				config.setSecurityMode(SecurityMode.required);
				config.setSASLAuthenticationEnabled(false);
				config.setCompressionEnabled(false);
				
				xmppManager.setConnection(new XMPPConnection(config));
				
				try{
					//connect to server
					connection.connect();
					Log.i(TAG, "XMPPConnect successfully..");
					
					ProviderManager.getInstance().addIQProvider(
							"notification", "androidpn:iq:notification", new NotificationIQProvider());
				}catch (XMPPException e) {
					Log.e(TAG, "XMPPConnect failed",e);
				}
				xmppManager.runTask();
			}else
			{
				Log.i(TAG, "XMPP Connect is already");
				xmppManager.runTask();
			}
			
		}
	}
	
	/**
	 * a runnable task to register the new user onto the server
	 */
	private class RegisterTask implements Runnable{
		
		XmppManager xmppManager = null;
		
		private RegisterTask()
		{
			xmppManager = XmppManager.this;
		}
		
		@Override
		public void run() {
			if(!xmppManager.isRegister())
			{
				final String newUserName = newRandomUUID();
				final String newPassword = newRandomUUID();
				
				Registration registration = new Registration();
				
				PacketFilter packetFilter = new AndFilter(new PacketIDFilter(
						registration.getPacketID()),new PacketTypeFilter(IQ.class));
				
				PacketListener packetListener = new PacketListener() {
					@Override
					public void processPacket(Packet packet) {
						Log.d("RegisterTask.packetListener", "processPacket...");
						Log.d("RegisterTask.packetListener", "processPacket...packet:"+packet.toXML());
						
						if(packet instanceof IQ)
						{
							IQ response = (IQ)packet;
							
							if(response.getType() == IQ.Type.ERROR)
							{
								if(!response.getError().toString().contains("409"))
								{
									Log.e(TAG, "unkonw error while registering XMPP account "
											+response.getError().getCondition());
								}
							}
							else if(response.getType() == IQ.Type.RESULT)
							{
								xmppManager.setUsername(newUserName);
								xmppManager.setPassword(newPassword);
								
								Log.d(TAG, "username:"+newUserName);
								Log.d(TAG, "password:"+newPassword);
								
								Editor editor = sharePre.edit();
								editor.putString(Constants.XMPP_USERNAME, newUserName);
								editor.putString(Constants.XMPP_PASSWORD, newPassword);
								editor.commit();
								
								Log.d(TAG, "account regist success");
								xmppManager.runTask();
							}
						}
					}
				};
			}
		}
		
	}
	
	/**
	 * a runnable task to login on server
	 *
	 */
	public class  LoginTask implements Runnable
	{
		private XmppManager xmppManager = null;
		
		public LoginTask()
		{
			this.xmppManager = XmppManager.this;
		}

		@Override
		public void run() {
			if(xmppManager.isAuthenticated())
			{
				Log.d(TAG, "username:"+username);
				Log.d(TAG, "password:"+password);
				
				try{
					xmppManager.getConnection().login(username, password, Constants.XMPP_RESOUCE_NAME);
					
					Log.d(TAG, "Login is successfully");
					
					if(xmppManager.getConnectionListener()!=null)
					{
						xmppManager.getConnection().addConnectionListener(
								xmppManager.getConnectionListener());
					}
					
					//packet filter
					PacketFilter packetFilter = new PacketTypeFilter(NotificationIQ.class);
					//packet listener
					PacketListener packetListener = xmppManager.getNotificationPacketListener();
					connection.addPacketListener(notificationPacketListener, packetFilter);
					
					xmppManager.getConnection().startKeepAliveThread2(xmppManager);
					
				
				}catch (XMPPException e) {
					Log.e(TAG, "LoginTask.run()... xmpp error");
					Log.e(TAG, "LoginTask.run()... cause by "+
							e.getMessage());
				
					if(e.getMessage()!=null
							&&e.getMessage().contains("401"))
					{
						xmppManager.registerAccount();
						return;
					}
					xmppManager.startReconnectionThread();
				}catch (Exception e) {
					Log.e(TAG, "LogintTask.run..other error");
					Log.e(TAG, "LoginTask.run....cause by"+
							e.getMessage());
					xmppManager.startReconnectionThread();
				}
				xmppManager.runTask();
			}else{
				xmppManager.runTask();
			}
		}
		
	}
	private String newRandomUUID() {
		String random = UUID.randomUUID().toString();
		return random.replace("-", "");
	}
	
	public void registerAccount() {
		removeAccount();
		submitLoginTask();
		runTask();
	}
	/**
	 * 清楚sharePreference里的账号.密码
	 */
	private void removeAccount() {

		Editor editor = sharePre.edit();
		editor.remove(Constants.XMPP_USERNAME);
		editor.remove(Constants.XMPP_PASSWORD);
		editor.commit();
		
	}
	/**
	 * 断开持久连接
	 */
	private void terminatePersistentConnection() {
		Log.d(TAG, "terminatePersistentConnection()...");
		Runnable runnable = new Runnable() {
			
			final XmppManager xmppManager =XmppManager.this;
			@Override
			public void run() {
				Log.d(TAG, "terminatePersistentConnection()..running..");
				xmppManager.getConnection().removePacketListener(
						xmppManager.getNotificationPacketListener());
				xmppManager.getConnection().disconnect();
				xmppManager.runTask();
			}
		};
		
		addTask(runnable);
	}
	private void submitLoginTask() {

		submitRegisterTask();
		addTask(new LoginTask());
		runTask();
	}
	private void submitRegisterTask() {
		submitConnectTask();
		addTask(new RegisterTask());
		runTask();
	}
	private void submitConnectTask() {
		addTask(new ConnectTask());
		runTask();
	}
	public void startReconnectionThread() {
		synchronized (reconnection) {
			if(!reconnection.isAlive())
			{
				reconnection.setName("xmpp Reconnect Thread");
				reconnection.start();
			}
		}
	}
	public boolean isAuthenticated() {
		return connection!=null&&connection.isConnected()
				&&connection.isAuthenticated();
	}
	public boolean isConnected() {
		
		return connection!=null&&connection.isConnected();
	}
	
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public XMPPConnection getConnection() {
		return connection;
	}
	public void setConnection(XMPPConnection connection) {
		this.connection = connection;
	}
	public ConnectionListener getConnectionListener() {
		return connectionListener;
	}
	public void setConnectionListener(ConnectionListener connectionListener) {
		this.connectionListener = connectionListener;
	}
	public NotificationPacketListener getNotificationPacketListener() {
		return notificationPacketListener;
	}
	public void setNotificationPacketListener(
			NotificationPacketListener notificationPacketListener) {
		this.notificationPacketListener = notificationPacketListener;
	}

	public Handler getHandler() {
		return handler;
	}
	public void setHandler(Handler handler) {
		this.handler = handler;
	}
}
